//
//  Color.hpp
//  RayTracer
//
//  Created by 俞云康 on 3/25/16.
//  Copyright © 2016 yushroom. All rights reserved.
//

#ifndef Color_hpp
#define Color_hpp

#include "Log.hpp"
#include "Math.hpp"

struct Color {
    union {
        struct {
            float r, g, b;
        };
        struct {
            float x, y, z;
        };
        float m[3];
    };
    
    Color() : x(0), y(0), z(0) { }
    Color(float x, float y, float z) : x(x), y(y), z(z) {
        Assert(valid());
    }
    Color(const Color& b) : x(b.x), y(b.y), z(b.z) { Assert(valid()); }
    
    inline float operator[](int idx) const {
        Assert(idx >= 0 && idx <= 2);
        return m[idx];
    }
    
    inline float& operator[](int idx) {
        Assert(idx >= 0 && idx <= 2);
        return m[idx];
    }
    
    Color operator-() const { return {-x, -y, -z}; }
    Color operator+(const float b)   const { return {x+b, y+b, z+b}; }
    Color operator-(const float b)   const { return {x-b, y-b, z-b}; }
    Color operator*(const float b)   const { return {x*b, y*b, z*b}; }
    Color operator/(const float b)   const { return {x/b, y/b, z/b}; }
    Color operator+(const Color& b) const { return {x+b.x, y+b.y, z+b.z}; }
    Color operator-(const Color& b) const { return {x-b.x, y-b.y, z-b.z}; }
    Color operator*(const Color& b) const { return {x*b.x, y*b.y, z*b.z}; }
    Color operator/(const Color& b) const { return {x/b.x, y/b.y, z/b.z}; }
    Color& operator+=(const Color& b) { x+=b.x; y+=b.y; z+=b.z; Assert(valid()); return *this; }
    Color& operator-=(const Color& b) { x-=b.x; y-=b.y; z-=b.z; Assert(valid()); return *this; }
    Color& operator*=(const Color& b) { x*=b.x; y*=b.y; z*=b.z; Assert(valid()); return *this; }
    Color& operator+=(const float b) { x+=b; y+=b; z+=b; Assert(valid()); return *this; }
    Color& operator-=(const float b) { x-=b; y-=b; z-=b; Assert(valid()); return *this; }
    Color& operator*=(const float b) { x*=b; y*=b; z*=b; Assert(valid()); return *this; }
	Color& operator/=(const float b) { x /= b; y /= b; z /= b; Assert(valid()); return *this; }
    friend Color operator*(float x, const Color& b) { return Color(x*b.x, x*b.y, x*b.z); }
    
    bool operator==(const Color& b) const { return (fabsf(x-b.x)<EPS && fabsf(y-b.y)<EPS && fabsf(z-b.z)<EPS); }
    bool operator!=(const Color& b) const { return (fabsf(x-b.x)>=EPS || fabsf(y-b.y)>=EPS || fabsf(z-b.z)>=EPS); }
    
    bool valid() const { return !(isnan(x)||isnan(y)||isnan(z)||isinf(x)||isinf(y)||isinf(z)); }
};


inline bool is_black(const Color& color) {
    return (fabsf(color.x)<EPS && fabsf(color.y)<EPS && fabsf(color.z)<EPS);
}

#endif /* Color_hpp */
